#ifndef ARRAYLIST_H_INCLUDED
#define ARRAYLIST_H_INCLUDED
#include <cstring>
#include <ostream>
#include <exception>
#include "List.h"

#define INIT_SIZE 100

template <typename T>
class ArrayList:public List<T>{
private:
    T* m_array;
    int m_maxsize;
    int m_cursize;
    int m_curposition;

private:
    bool IsSizeExtend(){
        if(this->m_cursize == this->m_maxsize){
            this->m_maxsize = this->m_maxsize * 2;
            try{
                T* newarray = new T[this->m_maxsize];
                memcpy(newarray, this->m_array, sizeof(T)*this->m_cursize);
            }catch(std::bad_alloc& e){
                this->m_maxsize = this->m_maxsize / 2;
                return false;
            }
            return true;
        }
        return false;

    }

public:
    ArrayList(){
        this->m_maxsize = INIT_SIZE;
        this->m_cursize = 0;
        this->m_curposition = -1;// start pos from 0
        try{
            this->m_array = new T[this->m_maxsize];
        }catch(std::bad_alloc& e){
            this->m_array = 0;
            this->m_maxsize = 0;
        }
    }

    ArrayList(const ArrayList< T >& arraylist){
        this->m_maxsize = arraylist.m_maxsize;
        this->m_cursize = arraylist.m_cursize;
        this->m_curposition = arraylist.m_curposition;
        try{
            this->m_array = new T[this->m_maxsize];
            std::memcpy(this->m_array, arraylist.m_array,sizeof(T)*this->m_maxsize);
        }catch(std::bad_alloc& e){
            this->m_maxsize = 0;
            this->m_curposition = -1;
            this->m_cursize = 0;
            this->m_array = 0;
            return ;
        }
    }

    ArrayList<T>& operator = (const ArrayList< T >& arraylist){
        this->m_maxsize = arraylist.m_maxsize;
        this->m_cursize = arraylist.m_cursize;
        this->m_curposition = arraylist.m_curposition;
        try{
            this->m_array = new T[this->m_maxsize];
            std::memcpy(this->m_array, arraylist.m_array,sizeof(T)*this->m_maxsize);
        }catch(std::bad_alloc& e){
            this->m_maxsize = 0;
            this->m_curposition = -1;
            this->m_cursize = 0;
            this->m_array = 0;
        }
        return *this;
    }

    virtual ~ArrayList(){
        if(this->m_array!=0){
            delete[] this->m_array;
        }
    }

    virtual void Clear(){
        this->m_curposition = -1;
        this->m_cursize = 0;
    }

    virtual bool IsEmpty(){
        return ( 0 == this->m_cursize );
    }

    virtual bool Append(const T& value){
        if(this->m_array == 0){
            return false;
        }
        if(this->IsSizeExtend() || (this->m_cursize < this->m_maxsize)){
            this->m_array[this->m_cursize] = value;
            this->m_cursize++;
            return true;
        }
        return false;
    }

    virtual bool Insert(const int position, const T& value){
        if(this->m_array == 0){
            return false;
        }
        if(position < this->m_cursize && position >= 0){
            if(this->IsSizeExtend() || (this->m_cursize < this->m_maxsize)){
                int pos = -1;
                for(pos = this->m_cursize; pos > position; pos --){
                    this->m_array[pos] = this->m_array[pos - 1];
                }
                this->m_array[pos] = value;
                this->m_cursize ++;
                return true;
            }
        }
        return false;
    }

    virtual bool Delete(const int position){
        if(this->m_array == 0){
            return false;
        }
        if(position < this->m_cursize && position >= 0){
            this->m_cursize--;
            for(int pos = position; pos < this->m_cursize; pos ++){
                this->m_array[pos] = this->m_array[pos + 1];
            }
            return true;
        }
        return false;
    }

    virtual bool GetValue(const int position, T& value){
        if(this->m_array == 0){
            return false;
        }
        if(position < this->m_cursize && position >= 0){
                value = this->m_array[position];
                return true;
        }
        return false;
    }

    virtual bool SetValue(const int position, const T& value){
        if(this->m_array == 0){
            return false;
        }
        if(position < this->m_cursize && position >= 0){
            this->m_array[position] = value;
            return true;
        }
        return false;
    }

    virtual bool GetPosition(int& position, const T& value){
        if(this->m_array == 0){
            return false;
        }
        if(position < this->m_cursize && position >= 0){
            for(int pos = 0; pos < this->m_cursize; pos ++){
                if(this->m_array[pos] == value){
                    position = pos;
                    return true;
                }
            }
        }
        return false;
    }

    virtual void DumpList(std::ostream& os){
        if(this->IsEmpty()){
            os<<"This is an empty list."<<"\n";
            return;
        }
        for(int pos = 0; pos < this->m_cursize; pos ++ ){
            os<<"["<<this->m_array[pos]<<"]";
        }
        os<<"\n";
    }
};

#endif // ARRAYLIST_H_INCLUDED
